#!/bin/bash

include "test.sh"
include "regex.sh"

declare arguments_include_cnt=0
function arguments()
{
    ((arguments_include_cnt++))
    eval "arguments_${arguments_include_cnt}_index=-1"
    eval "arguments_${arguments_include_cnt}_array=($*)"
    eval "arguments_${arguments_include_cnt}_length=$#"
    eval "arguments_${arguments_include_cnt}_skipped=()"
    eval "arguments_${arguments_include_cnt}_now=''"

    function arguments::is_end()
    {
        local index_name="arguments_${arguments_include_cnt}_index"
        local length_name="arguments_${arguments_include_cnt}_length"
        (( ${!index_name} < ${!length_name} ))
    }

    function arguments::next()
    {
        arguments::is_end
        luwh::assert $? == 0 "no more argument"

        eval "((arguments_${arguments_include_cnt}_index++))"
        local index_name="arguments_${arguments_include_cnt}_index"
        local index="${!index_name}"
        local array="arguments_${arguments_include_cnt}_array"
        eval "arguments_${arguments_include_cnt}_now=\"\${$array[$index]}\""
    }

    function arguments::skip()
    {
        eval "arguments_${arguments_include_cnt}_skipped+=('$(arguments::get)')"
        arguments::next
    }

    function arguments::skip_all()
    {
        while arguments::is_end; do
            arguments::skip
        done
    }

    function arguments::is_kv_option()
    {
        [[ "$(arguments::get)" =~ (^--${regex_id2}=) ]]
    }

    function arguments::is_option()
    {
        local now="$(arguments::get)"
        [ "${now:0:1}" == "-" ]
    }

    function arguments::get()
    {
        eval "echo \${arguments_${arguments_include_cnt}_now}"
    }

    function arguments::get_option()
    {
        local option="$(arguments::get)"
        while [ "${option:0:1}" == "-" ]; do
            option="${option:1}"
        done
        echo $option
    }

    function arguments::get_key()
    {
        arguments::is_kv_option
        luwh::assert $? == 0 "get_key need is_kv_option is true"

        local option="$(arguments::get)"
        option="${option:2}"
        echo ${option%%=*}
    }

    function arguments::get_val()
    {
        arguments::is_kv_option
        luwh::assert $? == 0 "get_val need is_kv_option is true"

        local option="$(arguments::get)"
        echo ${option#*=}
    }

    function arguments::get_skipped()
    {
        eval "echo \${arguments_${arguments_include_cnt}_skipped[*]}"
    }

    function arguments::destroy()
    {
        # delete variable
        unset "arguments_${arguments_include_cnt}_index"
        unset "arguments_${arguments_include_cnt}_array"
        unset "arguments_${arguments_include_cnt}_length"
        unset "arguments_${arguments_include_cnt}_skipped"
        unset "arguments_${arguments_include_cnt}_now"

        ((arguments_include_cnt--))

        # delete function
        if (( arguments_include_cnt == 0 )); then
            unset -f arguments::is_end
            unset -f arguments::next
            unset -f arguments::skip
            unset -f arguments::is_kv_option
            unset -f arguments::is_option
            unset -f arguments::get
            unset -f arguments::get_option
            unset -f arguments::get_key
            unset -f arguments::get_val
            unset -f arguments::get_skipped
            unset -f arguments::destroy
        fi
    }

    arguments::next
}

function arguments::test1()
{
    arguments -c --output a --index 2 --success --filter="123" normal

    # -c
    test::should_return_true arguments::is_end
    test::should_return_true arguments::is_option
    test::should_return_false arguments::is_kv_option
    test::run_cmd arguments::get
    test::should_be -c
    test::run_cmd arguments::get_option
    test::should_be c
    arguments::next

    # --output a
    test::should_return_true arguments::is_end
    test::should_return_true arguments::is_option
    test::should_return_false arguments::is_kv_option
    test::run_cmd arguments::get
    test::should_be --output
    test::run_cmd arguments::get_option
    test::should_be output
    arguments::next

    test::should_return_true arguments::is_end
    test::should_return_false arguments::is_option
    test::run_cmd arguments::get
    test::should_be a
    test::run_cmd arguments::get_option
    test::should_be a
    arguments::next

    # --index 2
    test::should_return_true arguments::is_end
    test::should_return_true arguments::is_option
    test::should_return_false arguments::is_kv_option
    arguments::skip

    test::should_return_true arguments::is_end
    test::should_return_false arguments::is_option
    arguments::skip

    test::run_cmd arguments::get_skipped
    test::should_be "--index 2"
    skipped=($(arguments::get_skipped))
    test::run_cmd echo ${skipped[0]}
    test::should_be --index

    # --success
    test::should_return_true arguments::is_end
    test::should_return_true arguments::is_option
    test::should_return_false arguments::is_kv_option
    test::run_cmd arguments::get
    test::should_be --success
    test::run_cmd arguments::get_option
    test::should_be success
    arguments::next

    # --filter="123"
    test::should_return_true arguments::is_end
    test::should_return_true arguments::is_option
    test::should_return_true arguments::is_kv_option
    test::run_cmd arguments::get
    test::should_be --filter="123"
    test::run_cmd arguments::get_key
    test::should_be filter
    test::run_cmd arguments::get_val
    test::should_be 123
    arguments::next

    # normal
    test::should_return_true arguments::is_end
    test::should_return_false arguments::is_option
    test::run_cmd arguments::get
    test::should_be normal
    arguments::next

    test::should_return_false arguments::is_end
    arguments::destroy
}
test::register_test arguments::test1

function arguments::test2()
{
    arguments -c
        arguments -a

        # -a
        test::should_return_true arguments::is_end
        test::should_return_true arguments::is_option
        test::run_cmd arguments::get
        test::should_be -a
        test::run_cmd arguments::get_option
        test::should_be a
        arguments::next

        test::should_return_false arguments::is_end
        arguments::destroy

    # -c
    test::should_return_true arguments::is_end
    test::should_return_true arguments::is_option
    test::run_cmd arguments::get
    test::should_be -c
    test::run_cmd arguments::get_option
    test::should_be c
    arguments::next

    test::should_return_false arguments::is_end
    arguments::destroy
}
test::register_test arguments::test2

function arguments::test3()
{
    arguments - --

    # -
    test::should_return_true arguments::is_end
    test::should_return_true arguments::is_option
    test::run_cmd arguments::get
    test::should_be -
    test::run_cmd arguments::get_option
    test::should_be ""
    arguments::next

    # -
    test::should_return_true arguments::is_end
    test::should_return_true arguments::is_option
    test::run_cmd arguments::get
    test::should_be --
    test::run_cmd arguments::get_option
    test::should_be ""
    arguments::next

    test::should_return_false arguments::is_end
    arguments::destroy
}
test::register_test arguments::test3
